#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>
#include <colors>

#define PLUGIN_VERSION	"1.0"
#define LOW_HEALTH_THRESHOLD	30	// 总血量≤30时触发闪烁

bool	g_bWasInBwState[MAXPLAYERS + 1];
bool	g_bIsFlashing[MAXPLAYERS + 1];
Handle	g_hFlashTimer[MAXPLAYERS + 1];

public Plugin myinfo = 
{
	name		= "l4d2_Survivor_Black_Color",
	author		= "豆瓣酱な",
	description	= "黑白状态改变玩家身体颜色.",
	version		= PLUGIN_VERSION,
	url		= "https://gitee.com/sweet-and-sour-bombs/adfvsdsd"
}

public void OnPluginStart()
{
	HookEvent("player_spawn",		Event_PlayerSpawn);
	HookEvent("revive_success",		Event_ReviveSuccess);
	HookEvent("heal_success",		Event_HealSuccess);
	HookEvent("player_hurt",		Event_PlayerHurt);
	
	AddCommandListener(Command_Give,	"give");
	
	for (int i = 1; i <= MaxClients; i++)
	{
		g_bWasInBwState[i]	= false;
		g_bIsFlashing[i]	= false;
		g_hFlashTimer[i]	= null;
	}
}

public void OnClientDisconnect(int client)
{
	g_bWasInBwState[client]	= false;
	g_bIsFlashing[client]	= false;
	
	if (g_hFlashTimer[client] != null)
	{
		KillTimer(g_hFlashTimer[client]);
		g_hFlashTimer[client] = null;
	}
}

public Action Command_Give(int client, const char[] command, int argc)
{
	if (argc < 1) return Plugin_Continue;
	
	char sArg[32];
	GetCmdArg(1, sArg, sizeof(sArg));
	
	if (StrEqual(sArg, "health", false))
	{
		int target = client;
		
		if (argc >= 2)
		{
			char sTarget[32];
			GetCmdArg(2, sTarget, sizeof(sTarget));
			
			int iTarget = FindTarget(client, sTarget, true, false);
			if (iTarget != -1)
			{
				target = iTarget;
			}
		}
		
		if (IsValidSurvivor(target))
		{
			CreateTimer(0.2, Timer_CheckBwState, GetClientUserId(target), TIMER_FLAG_NO_MAPCHANGE);
		}
	}
	return Plugin_Continue;
}

public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast)
{
	int client = GetClientOfUserId(event.GetInt("userid"));
	if (IsValidSurvivor(client))
	{
		g_bWasInBwState[client] = IsPlayerInBwState(client);
		CreateTimer(0.1, Timer_CheckBwState, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
	}
}

public void Event_ReviveSuccess(Event event, const char[] name, bool dontBroadcast)
{
	int client = GetClientOfUserId(event.GetInt("subject"));
	if (IsValidSurvivor(client))
	{
		CreateTimer(0.1, Timer_CheckBwState, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
	}
}

public void Event_HealSuccess(Event event, const char[] name, bool dontBroadcast)
{
	int client = GetClientOfUserId(event.GetInt("subject"));
	if (IsValidSurvivor(client))
	{
		CreateTimer(0.1, Timer_CheckBwState, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
	}
}

public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast)
{
	int client = GetClientOfUserId(event.GetInt("userid"));
	if (IsValidSurvivor(client))
	{
		CreateTimer(0.1, Timer_CheckBwState, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
	}
}

public Action Timer_CheckBwState(Handle timer, any userid)
{
	int client = GetClientOfUserId(userid);
	if (IsValidSurvivor(client))
	{
		CheckAndUpdateBwState(client);
	}
	return Plugin_Stop;
}

void CheckAndUpdateBwState(int client)
{
	char playerName[MAX_NAME_LENGTH];
	GetClientName(client, playerName, sizeof(playerName));
	
	bool bCurrentBwState	= IsPlayerInBwState(client);
	int iRealHealth		= GetEntProp(client, Prop_Send, "m_iHealth");	// 实血
	float fTempHealth	= GetTempHealth(client);				// 虚血
	int iTotalHealth	= RoundToFloor(iRealHealth + fTempHealth);		// 总血量
	
	if (bCurrentBwState && !g_bWasInBwState[client])
	{
		if (iTotalHealth <= LOW_HEALTH_THRESHOLD)
		{
			g_bIsFlashing[client] = true;
			if (g_hFlashTimer[client] == null)
			{
				g_hFlashTimer[client] = CreateTimer(0.5, Timer_FlashPlayer, GetClientUserId(client), TIMER_REPEAT);
			} 
		}
		else
		{
			SetEntityRenderColor(client, 0, 0, 0, 255);
			CPrintToChatAll("{default}[{red}警告{default}] {olive}%s {default}已进入黑白状态！", playerName);
		}
	}
	else if (!bCurrentBwState && g_bWasInBwState[client])
	{
		g_bIsFlashing[client] = false;
		if (g_hFlashTimer[client] != null)
		{
			KillTimer(g_hFlashTimer[client]);
			g_hFlashTimer[client] = null;
		}
		SetEntityRenderColor(client, 255, 255, 255, 255);
		CPrintToChatAll("{default}[{green}恢复{default}] {olive}%s {default}已从黑白状态恢复！", playerName);
	}
	else if (bCurrentBwState)
	{
		if (iTotalHealth <= LOW_HEALTH_THRESHOLD && !g_bIsFlashing[client])
		{
			g_bIsFlashing[client] = true;
			if (g_hFlashTimer[client] == null)
			{
				g_hFlashTimer[client] = CreateTimer(0.5, Timer_FlashPlayer, GetClientUserId(client), TIMER_REPEAT);
			}
		}
		else if (iTotalHealth > LOW_HEALTH_THRESHOLD && g_bIsFlashing[client])
		{
			g_bIsFlashing[client] = false;
			if (g_hFlashTimer[client] != null)
			{
				KillTimer(g_hFlashTimer[client]);
				g_hFlashTimer[client] = null;
			}
			SetEntityRenderColor(client, 0, 0, 0, 255); 
		}
	}
	else
	{
		SetEntityRenderColor(client, 255, 255, 255, 255);
	}
	
	g_bWasInBwState[client] = bCurrentBwState;
}

public Action Timer_FlashPlayer(Handle timer, any userid)
{
	int client = GetClientOfUserId(userid);
	if (IsValidSurvivor(client) && g_bIsFlashing[client] && IsPlayerInBwState(client))
	{
		int iRealHealth	= GetEntProp(client, Prop_Send, "m_iHealth");
		float fTempHealth	= GetTempHealth(client);
		int iTotalHealth	= RoundToFloor(iRealHealth + fTempHealth);
		
		if (iTotalHealth <= LOW_HEALTH_THRESHOLD)
		{
			static bool bToggle = false;
			bToggle = !bToggle;
			
			if (bToggle)	SetEntityRenderColor(client, 0, 0, 0, 255);	// 全黑
			else		SetEntityRenderColor(client, 0, 0, 0, 150);	// 全黑半透明
		}
		else
		{
			g_bIsFlashing[client] = false;
			SetEntityRenderColor(client, 0, 0, 0, 255);
		}
	}
	return Plugin_Continue;
}

// 获取临时血量（虚血）
float GetTempHealth(int client)
{
	float fTempHealth = GetEntPropFloat(client, Prop_Send, "m_healthBuffer");
	return (fTempHealth > 0.0) ? fTempHealth : 0.0;
}

bool IsPlayerInBwState(int client)
{
	return view_as<bool>(GetEntProp(client, Prop_Send, "m_bIsOnThirdStrike"));
}

bool IsValidSurvivor(int client)
{
	return (client > 0 && client <= MaxClients && IsClientInGame(client) && GetClientTeam(client) == 2);
}